#pragma once

#include "Common.hpp"
#include <list>
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace LogModule;

// 相当于一个抽象类，派生类必须重写才能实例化出对象
class UserInterface
{
public:
    virtual ~UserInterface() = default;
    virtual void SendTo(int sockfd, const string& message) = 0;
    virtual bool operator ==(const InetAddr& id) = 0;
    virtual string Id() = 0;
};

class User : public UserInterface
{
public:
    User(const InetAddr& id)
        :_id(id)
    {}

    void SendTo(int sockfd, const string& message) override  // override关键字表示如果没有重写父类的虚函数将会编译报错
    {
        LOG(LogLevel::DEBUG) << "send message to " << _id.Addr() << ", info: " << message;
        int n = ::sendto(sockfd, message.c_str(), message.size()-1, 0, _id.NetAddr(), _id.NetAddrLen());
        (void)n;
    }

    bool operator ==(const InetAddr& id) override
    {
        return _id == id;
    }

    string Id() override
    {
        return _id.Addr();
    }

    ~User(){}
private:
    InetAddr _id;
};

// 对用户消息作路由，将所有用户先管理起来
class UserManager
{
public:
    UserManager(){}
    
    // 添加用户（加入群聊）
    void AddUser(InetAddr& id)
    {
        LockGuard lockguard(_mutex);
        // 添加之前，肯定要看这个用户有没有存在这个list当中
        for(auto& user : _online_user)
        {   
            if(*user == id)
            {
                LOG(LogLevel::DEBUG) << id.Addr() << "：用户已经存在";
                return;
            }
        }
        LOG(LogLevel::DEBUG) << id.Addr() << "；新增该用户";
        _online_user.push_back(make_shared<User>(id));
        PrintUser();
    }

    // 删除用户（踢出群聊）
    void DelUser(InetAddr& id)
    {
        // version 1
        // 使用remove_if函数删除指定的用户（if存在就删除）
        // 但是这个函数没有真正的删除，只是将这个数放在了末尾
        auto pos = remove_if(_online_user.begin(), _online_user.end(), [&id](shared_ptr<UserInterface>& user){ return *user == id; });
        _online_user.erase(pos, _online_user.end());
        PrintUser();
        
        // version 2
        // for(auto user : _online_user)
        // {
        //     if(*user == id)
        //     {
        //         _online_user.erase(user);
        //         break;  // 必须要break，否则会有迭代器失效的问题
        //     }
        // }
    }

    // 实现消息转发，路由功能，将消息发送到每一个user当中
    void Router(int sockfd, const string& message)
    {
        LockGuard lockguard(_mutex);
        for(auto& user : _online_user)
        {
            // 相当于将一个message发送到群聊中的每一个用户中
            // 我们的 UserManager 不做发送，我们调用每一个User自己的SendTo方法，这种设计模式叫做 ：观察者模式 observer
            // 我们所有的User相当于一个观察者，加入到了_online_user(观察者数组)，一旦未来有事情发生了，你得通知我
            // 想要把消息发送给所有人，肯定是要遍历所有人的，比如qq的群聊有人数限制，不然会很慢
            user->SendTo(sockfd, message);
        }
    }

    void PrintUser()
    {
        for(auto user : _online_user)
        {
            LOG(LogLevel::DEBUG) <<"在线用户-> "<<  user->Id();
        }
    }

    ~UserManager(){}
private:
    // ？为什么用 UserInterface 而不是 User: 子类对象可以赋值给父类对象，会发生切片/切割，保留父类原有的那一部分代码
    list<shared_ptr<UserInterface>> _online_user;  // 存储在线用户
    Mutex _mutex;
};